/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
 
This file is part of Quake III Arena source code.
 
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
 
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_sky.c
#include "tr_local.h"

#define SKY_SUBDIVISIONS		8
#define HALF_SKY_SUBDIVISIONS	(SKY_SUBDIVISIONS/2)

static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2];
static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1];

/*
===================================================================================
 
POLYGON TO BOX SIDE PROJECTION
 
===================================================================================
*/

static vec3_t sky_clip[6] =
    {
        {1,1,0},
        {1,-1,0},
        {0,-1,1},
        {0,1,1},
        {1,0,1},
        {-1,0,1}
    };

static float	sky_mins[2][6], sky_maxs[2][6];
static float	sky_min, sky_max;

/*
================
AddSkyPolygon
================
*/
static void AddSkyPolygon (int nump, vec3_t vecs)
{
    int		i,j;
    vec3_t	v, av;
    float	s, t, dv;
    int		axis;
    float	*vp;
    // s = [0]/[2], t = [1]/[2]
    static int	vec_to_st[6][3] =
        {
            {-2,3,1},
            {2,3,-1},

            {1,3,2},
            {-1,3,-2},

            {-2,-1,3},
            {-2,1,-3}

            //	{-1,2,3},
            //	{1,2,-3}
        };

    // decide which face it maps to
    VectorCopy (vec3_origin, v);
    for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
    {
        VectorAdd (vp, v, v);
    }
    av[0] = fabs(v[0]);
    av[1] = fabs(v[1]);
    av[2] = fabs(v[2]);
    if (av[0] > av[1] && av[0] > av[2])
    {
        if (v[0] < 0)
            axis = 1;
        else
            axis = 0;
    }
    else if (av[1] > av[2] && av[1] > av[0])
    {
        if (v[1] < 0)
            axis = 3;
        else
            axis = 2;
    }
    else
    {
        if (v[2] < 0)
            axis = 5;
        else
            axis = 4;
    }

    // project new texture coords
    for (i=0 ; i<nump ; i++, vecs+=3)
    {
        j = vec_to_st[axis][2];
        if (j > 0)
            dv = vecs[j - 1];
        else
            dv = -vecs[-j - 1];
        if (dv < 0.001)
            continue;	// don't divide by zero
        j = vec_to_st[axis][0];
        if (j < 0)
            s = -vecs[-j -1] / dv;
        else
            s = vecs[j-1] / dv;
        j = vec_to_st[axis][1];
        if (j < 0)
            t = -vecs[-j -1] / dv;
        else
            t = vecs[j-1] / dv;

        if (s < sky_mins[0][axis])
            sky_mins[0][axis] = s;
        if (t < sky_mins[1][axis])
            sky_mins[1][axis] = t;
        if (s > sky_maxs[0][axis])
            sky_maxs[0][axis] = s;
        if (t > sky_maxs[1][axis])
            sky_maxs[1][axis] = t;
    }
}

#define	ON_EPSILON		0.1f			// point on plane side epsilon
#define	MAX_CLIP_VERTS	64
/*
================
ClipSkyPolygon
================
*/
static void ClipSkyPolygon (int nump, vec3_t vecs, int stage)
{
    float	*norm;
    float	*v;
    qboolean	front, back;
    float	d, e;
    float	dists[MAX_CLIP_VERTS];
    int		sides[MAX_CLIP_VERTS];
    vec3_t	newv[2][MAX_CLIP_VERTS];
    int		newc[2];
    int		i, j;

    if (nump > MAX_CLIP_VERTS-2)
        Com_Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS");
    if (stage == 6)
    {	// fully clipped, so draw it
        AddSkyPolygon (nump, vecs);
        return;
    }

    front = back = qfalse;
    norm = sky_clip[stage];
    for (i=0, v = vecs ; i<nump ; i++, v+=3)
    {
        d = DotProduct (v, norm);
        if (d > ON_EPSILON)
        {
            front = qtrue;
            sides[i] = SIDE_FRONT;
        }
        else if (d < -ON_EPSILON)
        {
            back = qtrue;
            sides[i] = SIDE_BACK;
        }
        else
            sides[i] = SIDE_ON;
        dists[i] = d;
    }

    if (!front || !back)
    {	// not clipped
        ClipSkyPolygon (nump, vecs, stage+1);
        return;
    }

    // clip it
    sides[i] = sides[0];
    dists[i] = dists[0];
    VectorCopy (vecs, (vecs+(i*3)) );
    newc[0] = newc[1] = 0;

    for (i=0, v = vecs ; i<nump ; i++, v+=3)
    {
        switch (sides[i])
        {
        case SIDE_FRONT:
            VectorCopy (v, newv[0][newc[0]]);
            newc[0]++;
            break;
        case SIDE_BACK:
            VectorCopy (v, newv[1][newc[1]]);
            newc[1]++;
            break;
        case SIDE_ON:
            VectorCopy (v, newv[0][newc[0]]);
            newc[0]++;
            VectorCopy (v, newv[1][newc[1]]);
            newc[1]++;
            break;
        }

        if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
            continue;

        d = dists[i] / (dists[i] - dists[i+1]);
        for (j=0 ; j<3 ; j++)
        {
            e = v[j] + d*(v[j+3] - v[j]);
            newv[0][newc[0]][j] = e;
            newv[1][newc[1]][j] = e;
        }
        newc[0]++;
        newc[1]++;
    }

    // continue
    ClipSkyPolygon (newc[0], newv[0][0], stage+1);
    ClipSkyPolygon (newc[1], newv[1][0], stage+1);
}

/*
==============
ClearSkyBox
==============
*/
static void ClearSkyBox (void)
{
    int		i;

    for (i=0 ; i<6 ; i++)
    {
        sky_mins[0][i] = sky_mins[1][i] = 9999;
        sky_maxs[0][i] = sky_maxs[1][i] = -9999;
    }
}

/*
================
RB_ClipSkyPolygons
================
*/
void RB_ClipSkyPolygons( shaderCommands_t *input )
{
    vec3_t		p[5];	// need one extra point for clipping
    int			i, j;

    ClearSkyBox();

    for ( i = 0; i < input->numIndexes; i += 3 )
    {
        for (j = 0 ; j < 3 ; j++)
        {
            VectorSubtract( input->xyz[input->indexes[i+j]],
                            backEnd.viewParms.ori.origin,
                            p[j] );
        }
        ClipSkyPolygon( 3, p[0], 0 );
    }
}

/*
===================================================================================
 
CLOUD VERTEX GENERATION
 
===================================================================================
*/

/*
** MakeSkyVec
**
** Parms: s, t range from -1 to 1
*/
static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ )
{
    // 1 = s, 2 = t, 3 = 2048
    static int	st_to_vec[6][3] =
        {
            {3,-1,2},
            {-3,1,2},

            {1,3,2},
            {-1,-3,2},

            {-2,-1,3},		// 0 degrees yaw, look straight up
            {2,-1,-3}		// look straight down
        };

    vec3_t		b;
    int			j, k;
    float	boxSize;

    boxSize = backEnd.viewParms.zFar / 1.75;		// div sqrt(3)
    b[0] = s*boxSize;
    b[1] = t*boxSize;
    b[2] = boxSize;

    for (j=0 ; j<3 ; j++)
    {
        k = st_to_vec[axis][j];
        if (k < 0)
        {
            outXYZ[j] = -b[-k - 1];
        }
        else
        {
            outXYZ[j] = b[k - 1];
        }
    }

    // avoid bilerp seam
    s = (s+1)*0.5;
    t = (t+1)*0.5;
    if (s < sky_min)
    {
        s = sky_min;
    }
    else if (s > sky_max)
    {
        s = sky_max;
    }

    if (t < sky_min)
    {
        t = sky_min;
    }
    else if (t > sky_max)
    {
        t = sky_max;
    }

    t = 1.0 - t;


    if ( outSt )
    {
        outSt[0] = s;
        outSt[1] = t;
    }
}

static int	sky_texorder[6] = {0,2,1,3,4,5};
static vec3_t	s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1];
static float	s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2];

static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] )
{
    int s, t;

    GL_Bind( image );

    for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t < maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
    {
        qglBegin( GL_TRIANGLE_STRIP );

        for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ )
        {
            qglTexCoord2fv( s_skyTexCoords[t][s] );
            qglVertex3fv( s_skyPoints[t][s] );

            qglTexCoord2fv( s_skyTexCoords[t+1][s] );
            qglVertex3fv( s_skyPoints[t+1][s] );
        }

        qglEnd();
    }
}

static void DrawSkyBox( shader_t *shader )
{
    int		i;

    sky_min = 0;
    sky_max = 1;

    Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) );

    for (i=0 ; i<6 ; i++)
    {
        int sky_mins_subd[2], sky_maxs_subd[2];
        int s, t;

        sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
        sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
        sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
        sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;

        if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
                ( sky_mins[1][i] >= sky_maxs[1][i] ) )
        {
            continue;
        }

        sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS;
        sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS;
        sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS;
        sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS;

        if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
        else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
        if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS;
        else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;

        if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
        else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
        if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS;
        else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;

        //
        // iterate through the subdivisions
        //
        for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ )
        {
            for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ )
            {
                MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
                            ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
                            i,
                            s_skyTexCoords[t][s],
                            s_skyPoints[t][s] );
            }
        }

        DrawSkySide( shader->sky.outerbox[sky_texorder[i]],
                     sky_mins_subd,
                     sky_maxs_subd );
    }

}

static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes )
{
    int s, t;
    int vertexStart = tess.numVertexes;
    int tHeight, sWidth;

    tHeight = maxs[1] - mins[1] + 1;
    sWidth = maxs[0] - mins[0] + 1;

    for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
    {
        for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ )
        {
            VectorAdd( s_skyPoints[t][s], backEnd.viewParms.ori.origin, tess.xyz[tess.numVertexes] );
            tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0];
            tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1];

            tess.numVertexes++;

            if ( tess.numVertexes >= SHADER_MAX_VERTEXES )
            {
                Com_Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()\n" );
            }
        }
    }

    // only add indexes for one pass, otherwise it would draw multiple times for each pass
    if ( addIndexes )
    {
        for ( t = 0; t < tHeight-1; t++ )
        {
            for ( s = 0; s < sWidth-1; s++ )
            {
                tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth );
                tess.numIndexes++;
                tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth );
                tess.numIndexes++;
                tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth );
                tess.numIndexes++;

                tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth );
                tess.numIndexes++;
                tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth );
                tess.numIndexes++;
                tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth );
                tess.numIndexes++;
            }
        }
    }
}

static void FillCloudBox( const shader_t *shader, int stage )
{
    int i;

    for ( i =0; i < 6; i++ )
    {
        int sky_mins_subd[2], sky_maxs_subd[2];
        int s, t;
        float MIN_T;

        if ( 1 ) // FIXME? shader->sky.fullClouds )
        {
            MIN_T = -HALF_SKY_SUBDIVISIONS;

            // still don't want to draw the bottom, even if fullClouds
            if ( i == 5 )
                continue;
        }
        else
        {
            switch( i )
            {
            case 0:
            case 1:
            case 2:
            case 3:
                MIN_T = -1;
                break;
            case 5:
                // don't draw clouds beneath you
                continue;
            case 4:		// top
            default:
                MIN_T = -HALF_SKY_SUBDIVISIONS;
                break;
            }
        }

        sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
        sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
        sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
        sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;

        if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
                ( sky_mins[1][i] >= sky_maxs[1][i] ) )
        {
            continue;
        }

        sky_mins_subd[0] = (long)( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS );
        sky_mins_subd[1] = (long)( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS );
        sky_maxs_subd[0] = (long)( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS );
        sky_maxs_subd[1] = (long)( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS );

        if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
        else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
        if ( sky_mins_subd[1] < MIN_T )
            sky_mins_subd[1] = MIN_T;
        else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS )
            sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;

        if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
        else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
        if ( sky_maxs_subd[1] < MIN_T )
            sky_maxs_subd[1] = MIN_T;
        else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
            sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;

        //
        // iterate through the subdivisions
        //
        for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ )
        {
            for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ )
            {
                MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
                            ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
                            i,
                            NULL,
                            s_skyPoints[t][s] );

                s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0];
                s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1];
            }
        }

        // only add indexes for first stage
        FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) );
    }
}

/*
** R_BuildCloudData
*/
void R_BuildCloudData( shaderCommands_t *input )
{
    int			i;
    shader_t	*shader;

    shader = input->shader;

    assert( shader->isSky );

    sky_min = 1.0 / 256.0f;		// FIXME: not correct?
    sky_max = 255.0 / 256.0f;

    // set up for drawing
    tess.numIndexes = 0;
    tess.numVertexes = 0;

    if ( input->shader->sky.cloudHeight )
    {
        for ( i = 0; i < MAX_SHADER_STAGES; i++ )
        {
            if ( !tess.xstages[i] )
            {
                break;
            }
            FillCloudBox( input->shader, i );
        }
    }
}

/*
** R_InitSkyTexCoords
** Called when a sky shader is parsed
*/
#define SQR( a ) ((a)*(a))
void R_InitSkyTexCoords( float heightCloud )
{
    int i, s, t;
    float radiusWorld = 4096;
    float p;
    float sRad, tRad;
    vec3_t skyVec;
    vec3_t v;

    // init zfar so MakeSkyVec works even though
    // a world hasn't been bounded
    backEnd.viewParms.zFar = 1024;

    for ( i = 0; i < 6; i++ )
    {
        for ( t = 0; t <= SKY_SUBDIVISIONS; t++ )
        {
            for ( s = 0; s <= SKY_SUBDIVISIONS; s++ )
            {
                // compute vector from view origin to sky side integral point
                MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
                            ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
                            i,
                            NULL,
                            skyVec );

                // compute parametric value 'p' that intersects with cloud layer
                p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) *
                    ( -2 * skyVec[2] * radiusWorld +
                      2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) +
                                2 * SQR( skyVec[0] ) * radiusWorld * heightCloud +
                                SQR( skyVec[0] ) * SQR( heightCloud ) +
                                2 * SQR( skyVec[1] ) * radiusWorld * heightCloud +
                                SQR( skyVec[1] ) * SQR( heightCloud ) +
                                2 * SQR( skyVec[2] ) * radiusWorld * heightCloud +
                                SQR( skyVec[2] ) * SQR( heightCloud ) ) );

                s_cloudTexP[i][t][s] = p;

                // compute intersection point based on p
                VectorScale( skyVec, p, v );
                v[2] += radiusWorld;

                // compute vector from world origin to intersection point 'v'
                VectorNormalize( v );

                sRad = Q_acos( v[0] );
                tRad = Q_acos( v[1] );

                s_cloudTexCoords[i][t][s][0] = sRad;
                s_cloudTexCoords[i][t][s][1] = tRad;
            }
        }
    }
}

//======================================================================================

/*
** RB_DrawSun
*/
void RB_DrawSun( void )
{
    float		size;
    float		dist;
    vec3_t		origin, vec1, vec2;
    vec3_t		temp;

    if ( !backEnd.skyRenderedThisView )
    {
        return;
    }
    if ( !r_drawSun->integer )
    {
        return;
    }
    qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
    qglTranslatef (backEnd.viewParms.ori.origin[0], backEnd.viewParms.ori.origin[1], backEnd.viewParms.ori.origin[2]);

    dist = 	backEnd.viewParms.zFar / 1.75;		// div sqrt(3)
    size = dist * 0.4;

    VectorScale( tr.sunDirection, dist, origin );
    PerpendicularVector( vec1, tr.sunDirection );
    CrossProduct( tr.sunDirection, vec1, vec2 );

    VectorScale( vec1, size, vec1 );
    VectorScale( vec2, size, vec2 );

    // farthest depth range
    qglDepthRange( 1.0, 1.0 );

    // FIXME: use quad stamp
    RB_BeginSurface( tr.sunShader, tess.fogNum );
    VectorCopy( origin, temp );
    VectorSubtract( temp, vec1, temp );
    VectorSubtract( temp, vec2, temp );
    VectorCopy( temp, tess.xyz[tess.numVertexes] );
    tess.texCoords[tess.numVertexes][0][0] = 0;
    tess.texCoords[tess.numVertexes][0][1] = 0;
    tess.vertexColors[tess.numVertexes][0] = 1.0;
    tess.vertexColors[tess.numVertexes][1] = 1.0;
    tess.vertexColors[tess.numVertexes][2] = 1.0;
    tess.vertexColors[tess.numVertexes][3] = 1.0;
    tess.numVertexes++;

    VectorCopy( origin, temp );
    VectorAdd( temp, vec1, temp );
    VectorSubtract( temp, vec2, temp );
    VectorCopy( temp, tess.xyz[tess.numVertexes] );
    tess.texCoords[tess.numVertexes][0][0] = 0;
    tess.texCoords[tess.numVertexes][0][1] = 1;
    tess.vertexColors[tess.numVertexes][0] = 1.0;
    tess.vertexColors[tess.numVertexes][1] = 1.0;
    tess.vertexColors[tess.numVertexes][2] = 1.0;
    tess.vertexColors[tess.numVertexes][3] = 1.0;
    tess.numVertexes++;

    VectorCopy( origin, temp );
    VectorAdd( temp, vec1, temp );
    VectorAdd( temp, vec2, temp );
    VectorCopy( temp, tess.xyz[tess.numVertexes] );
    tess.texCoords[tess.numVertexes][0][0] = 1;
    tess.texCoords[tess.numVertexes][0][1] = 1;
    tess.vertexColors[tess.numVertexes][0] = 1.0;
    tess.vertexColors[tess.numVertexes][1] = 1.0;
    tess.vertexColors[tess.numVertexes][2] = 1.0;
    tess.vertexColors[tess.numVertexes][3] = 1.0;
    tess.numVertexes++;

    VectorCopy( origin, temp );
    VectorSubtract( temp, vec1, temp );
    VectorAdd( temp, vec2, temp );
    VectorCopy( temp, tess.xyz[tess.numVertexes] );
    tess.texCoords[tess.numVertexes][0][0] = 1;
    tess.texCoords[tess.numVertexes][0][1] = 0;
    tess.vertexColors[tess.numVertexes][0] = 1.0;
    tess.vertexColors[tess.numVertexes][1] = 1.0;
    tess.vertexColors[tess.numVertexes][2] = 1.0;
    tess.vertexColors[tess.numVertexes][3] = 1.0;
    tess.numVertexes++;

    tess.indexes[tess.numIndexes++] = 0;
    tess.indexes[tess.numIndexes++] = 1;
    tess.indexes[tess.numIndexes++] = 2;
    tess.indexes[tess.numIndexes++] = 0;
    tess.indexes[tess.numIndexes++] = 2;
    tess.indexes[tess.numIndexes++] = 3;

    RB_EndSurface();

    // back to normal depth range
    qglDepthRange( 0.0, 1.0 );
}




/*
================
RB_StageIteratorSky
 
All of the visible sky triangles are in tess
 
Other things could be stuck in here, like birds in the sky, etc
================
*/
void RB_StageIteratorSky( void )
{
    if ( r_fastsky->integer )
    {
        return;
    }

    // go through all the polygons and project them onto
    // the sky box to see which blocks on each side need
    // to be drawn
    RB_ClipSkyPolygons( &tess );

    // r_showsky will let all the sky blocks be drawn in
    // front of everything to allow developers to see how
    // much sky is getting sucked in
    if ( r_showsky->integer )
    {
        qglDepthRange( 0.0, 0.0 );
    }
    else
    {
        qglDepthRange( 1.0, 1.0 );
    }

    // draw the outer skybox
    if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage )
    {
        qglColor4f( tr.identityLight, tr.identityLight, tr.identityLight, 1.0 );

        qglPushMatrix ();
        GL_State( 0 );
        qglTranslatef (backEnd.viewParms.ori.origin[0], backEnd.viewParms.ori.origin[1], backEnd.viewParms.ori.origin[2]);

        DrawSkyBox( tess.shader );

        qglPopMatrix();
    }

    // generate the vertexes for all the clouds, which will be drawn
    // by the generic shader routine
    R_BuildCloudData( &tess );

    RB_StageIteratorGeneric();

    // draw the inner skybox


    // back to normal depth range
    qglDepthRange( 0.0, 1.0 );

    // note that sky was drawn so we will draw a sun later
    backEnd.skyRenderedThisView = qtrue;
}

